using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;

namespace MT5Trade.Models
{
    /// <summary>
    /// 历史交易数据统计分析类
    /// 实现严格的统计学方法，为Kelly公式提供准确的参数输入
    /// 包含数据清洗、胜率计算、盈亏比分析和滑动窗口统计功能
    /// </summary>
    public class HistoricalAnalyzer
    {
        #region 常量定义

        /// <summary>
        /// 最小样本数量要求（统计显著性）
        /// </summary>
        public const int MIN_SAMPLE_SIZE = 30;

        /// <summary>
        /// 默认滑动窗口大小（天数）
        /// </summary>
        public const int DEFAULT_WINDOW_DAYS = 30;

        /// <summary>
        /// 异常交易检测阈值（标准差倍数）
        /// </summary>
        public const double OUTLIER_THRESHOLD = 3.0;

        /// <summary>
        /// 计算精度要求
        /// </summary>
        public const double CALCULATION_PRECISION = 0.0001;

        #endregion

        #region 数据清洗方法

        /// <summary>
        /// 清洗历史交易数据，过滤异常交易和处理缺失数据
        /// </summary>
        /// <param name="rawTrades">原始交易数据</param>
        /// <returns>清洗后的交易数据</returns>
        /// <exception cref="ArgumentException">数据无效时抛出异常</exception>
        public IEnumerable<TradeModel> CleanHistoricalData(IEnumerable<TradeModel> rawTrades)
        {
            if (rawTrades == null)
                throw new ArgumentNullException(nameof(rawTrades), "原始交易数据不能为空");

            var trades = rawTrades.ToList();
            if (trades.Count == 0)
                throw new ArgumentException("交易数据不能为空", nameof(rawTrades));

            var cleanedTrades = new List<TradeModel>();

            // 1. 过滤基础数据完整性
            var validTrades = trades.Where(trade => 
                trade != null &&
                trade.Ticket > 0 &&
                !string.IsNullOrEmpty(trade.Symbol) &&
                trade.Time != default(DateTime) &&
                !double.IsNaN(trade.Price) &&
                !double.IsInfinity(trade.Price) &&
                trade.Price > 0
            ).ToList();

            if (validTrades.Count < MIN_SAMPLE_SIZE)
                throw new ArgumentException($"有效交易数量{validTrades.Count}不足最小样本要求{MIN_SAMPLE_SIZE}");

            // 2. 计算净盈亏用于异常值检测
            var netProfits = validTrades.Select(trade => 
                trade.Profit + trade.Swap + trade.Commission + trade.Fee).ToList();

            // 3. 异常值检测和过滤（基于3-sigma规则）
            double mean = netProfits.Average();
            double variance = netProfits.Sum(x => Math.Pow(x - mean, 2)) / netProfits.Count;
            double stdDev = Math.Sqrt(variance);

            foreach (var trade in validTrades)
            {
                double netProfit = trade.Profit + trade.Swap + trade.Commission + trade.Fee;
                double zScore = stdDev > 0 ? Math.Abs(netProfit - mean) / stdDev : 0;

                // 保留3-sigma范围内的交易
                if (zScore <= OUTLIER_THRESHOLD)
                {
                    cleanedTrades.Add(trade);
                }
            }

            return cleanedTrades.OrderBy(trade => trade.Time);
        }

        /// <summary>
        /// 按时间窗口过滤交易数据
        /// </summary>
        /// <param name="trades">交易数据</param>
        /// <param name="windowDays">时间窗口（天数）</param>
        /// <param name="endDate">结束日期（默认为当前时间）</param>
        /// <returns>时间窗口内的交易数据</returns>
        public IEnumerable<TradeModel> FilterByTimeWindow(IEnumerable<TradeModel> trades, 
            int windowDays, DateTime? endDate = null)
        {
            if (trades == null)
                throw new ArgumentNullException(nameof(trades));

            if (windowDays <= 0)
                throw new ArgumentException("时间窗口必须大于0", nameof(windowDays));

            DateTime cutoffTime = (endDate ?? DateTime.Now).AddDays(-windowDays);
            return trades.Where(trade => trade.Time >= cutoffTime);
        }

        #endregion

        #region 胜率计算方法

        /// <summary>
        /// 计算交易胜率
        /// 胜率 = 盈利交易数 / 总交易数
        /// </summary>
        /// <param name="trades">交易数据</param>
        /// <returns>胜率，范围[0,1]</returns>
        public double CalculateWinRate(IEnumerable<TradeModel> trades)
        {
            if (trades == null)
                throw new ArgumentNullException(nameof(trades), "交易数据不能为空");

            var tradeList = trades.ToList();
            if (tradeList.Count == 0)
                throw new ArgumentException("交易数据不能为空", nameof(trades));

            // 计算净盈亏确定盈利交易
            var profitableTrades = tradeList.Count(trade =>
            {
                double netProfit = trade.Profit + trade.Swap + trade.Commission + trade.Fee;
                return netProfit > 0;
            });

            double winRate = (double)profitableTrades / tradeList.Count;

            // 验证计算结果
            if (winRate < 0 || winRate > 1)
                throw new InvalidOperationException($"计算得出的胜率{winRate:F6}超出有效范围[0,1]");

            return winRate;
        }

        /// <summary>
        /// 计算分段胜率（按时间段统计）
        /// </summary>
        /// <param name="trades">交易数据</param>
        /// <param name="segmentDays">时间段天数</param>
        /// <returns>分段胜率列表</returns>
        public List<(DateTime StartDate, DateTime EndDate, double WinRate, int TradeCount)> 
            CalculateSegmentedWinRates(IEnumerable<TradeModel> trades, int segmentDays = 7)
        {
            if (trades == null)
                throw new ArgumentNullException(nameof(trades));

            var tradeList = trades.OrderBy(t => t.Time).ToList();
            if (tradeList.Count == 0)
                return new List<(DateTime, DateTime, double, int)>();

            var results = new List<(DateTime, DateTime, double, int)>();
            var startDate = tradeList.First().Time.Date;
            var endDate = tradeList.Last().Time.Date;

            for (var currentStart = startDate; currentStart <= endDate; currentStart = currentStart.AddDays(segmentDays))
            {
                var currentEnd = currentStart.AddDays(segmentDays).AddSeconds(-1);
                var segmentTrades = tradeList.Where(t => t.Time >= currentStart && t.Time <= currentEnd).ToList();

                if (segmentTrades.Count > 0)
                {
                    double segmentWinRate = CalculateWinRate(segmentTrades);
                    results.Add((currentStart, currentEnd, segmentWinRate, segmentTrades.Count));
                }
            }

            return results;
        }

        #endregion

        #region 盈亏比计算方法

        /// <summary>
        /// 计算平均盈亏比
        /// 盈亏比 = 平均盈利 / |平均亏损|
        /// </summary>
        /// <param name="trades">交易数据</param>
        /// <returns>盈亏比</returns>
        public double CalculateProfitLossRatio(IEnumerable<TradeModel> trades)
        {
            if (trades == null)
                throw new ArgumentNullException(nameof(trades), "交易数据不能为空");

            var tradeList = trades.ToList();
            if (tradeList.Count == 0)
                throw new ArgumentException("交易数据不能为空", nameof(trades));

            // 分离盈利和亏损交易
            var profitableTrades = new List<double>();
            var losingTrades = new List<double>();

            foreach (var trade in tradeList)
            {
                double netProfit = trade.Profit + trade.Swap + trade.Commission + trade.Fee;
                
                if (netProfit > 0)
                    profitableTrades.Add(netProfit);
                else if (netProfit < 0)
                    losingTrades.Add(Math.Abs(netProfit)); // 存储亏损的绝对值
            }

            // 处理极端情况
            if (losingTrades.Count == 0)
            {
                return profitableTrades.Count > 0 ? double.MaxValue : 0;
            }

            if (profitableTrades.Count == 0)
            {
                return 0; // 没有盈利交易
            }

            // 计算平均盈利和平均亏损
            double avgProfit = profitableTrades.Average();
            double avgLoss = losingTrades.Average();

            if (avgLoss < CALCULATION_PRECISION)
                throw new InvalidOperationException("平均亏损过小，无法计算有效盈亏比");

            return avgProfit / avgLoss;
        }

        /// <summary>
        /// 计算盈利交易和亏损交易的详细统计信息
        /// </summary>
        /// <param name="trades">交易数据</param>
        /// <returns>详细统计结果</returns>
        public ProfitLossStatistics CalculateDetailedProfitLossStats(IEnumerable<TradeModel> trades)
        {
            if (trades == null)
                throw new ArgumentNullException(nameof(trades));

            var tradeList = trades.ToList();
            var profitableTrades = new List<double>();
            var losingTrades = new List<double>();

            foreach (var trade in tradeList)
            {
                double netProfit = trade.Profit + trade.Swap + trade.Commission + trade.Fee;
                
                if (netProfit > 0)
                    profitableTrades.Add(netProfit);
                else if (netProfit < 0)
                    losingTrades.Add(netProfit); // 保留负号
            }

            return new ProfitLossStatistics
            {
                TotalTrades = tradeList.Count,
                ProfitableTrades = profitableTrades.Count,
                LosingTrades = losingTrades.Count,
                BreakEvenTrades = tradeList.Count - profitableTrades.Count - losingTrades.Count,
                
                AverageProfit = profitableTrades.Count > 0 ? profitableTrades.Average() : 0,
                AverageLoss = losingTrades.Count > 0 ? Math.Abs(losingTrades.Average()) : 0,
                
                MaxProfit = profitableTrades.Count > 0 ? profitableTrades.Max() : 0,
                MaxLoss = losingTrades.Count > 0 ? Math.Abs(losingTrades.Min()) : 0,
                
                TotalProfit = profitableTrades.Sum(),
                TotalLoss = Math.Abs(losingTrades.Sum()),
                
                NetProfit = profitableTrades.Sum() + losingTrades.Sum(),
                ProfitFactor = losingTrades.Count > 0 ? profitableTrades.Sum() / Math.Abs(losingTrades.Sum()) : double.MaxValue
            };
        }

        #endregion

        #region 滑动窗口分析方法

        /// <summary>
        /// 滑动窗口分析，计算不同时间窗口的胜率和盈亏比
        /// </summary>
        /// <param name="trades">交易数据（必须按时间排序）</param>
        /// <param name="windowDays">滑动窗口大小（天数）</param>
        /// <param name="stepDays">滑动步长（天数）</param>
        /// <returns>滑动窗口分析结果</returns>
        public List<SlidingWindowResult> PerformSlidingWindowAnalysis(
            IEnumerable<TradeModel> trades, int windowDays = DEFAULT_WINDOW_DAYS, int stepDays = 1)
        {
            if (trades == null)
                throw new ArgumentNullException(nameof(trades));

            if (windowDays <= 0)
                throw new ArgumentException("窗口大小必须大于0", nameof(windowDays));

            if (stepDays <= 0)
                throw new ArgumentException("步长必须大于0", nameof(stepDays));

            var tradeList = trades.OrderBy(t => t.Time).ToList();
            if (tradeList.Count < MIN_SAMPLE_SIZE)
                throw new ArgumentException($"交易数据不足最小样本要求{MIN_SAMPLE_SIZE}");

            var results = new List<SlidingWindowResult>();
            var startDate = tradeList.First().Time.Date;
            var endDate = tradeList.Last().Time.Date;

            for (var windowStart = startDate; 
                 windowStart.AddDays(windowDays) <= endDate; 
                 windowStart = windowStart.AddDays(stepDays))
            {
                var windowEnd = windowStart.AddDays(windowDays);
                var windowTrades = tradeList.Where(t => 
                    t.Time.Date >= windowStart && t.Time.Date < windowEnd).ToList();

                if (windowTrades.Count >= MIN_SAMPLE_SIZE)
                {
                    try
                    {
                        var winRate = CalculateWinRate(windowTrades);
                        var profitLossRatio = CalculateProfitLossRatio(windowTrades);
                        var stats = CalculateDetailedProfitLossStats(windowTrades);

                        results.Add(new SlidingWindowResult
                        {
                            WindowStart = windowStart,
                            WindowEnd = windowEnd.AddSeconds(-1),
                            TradeCount = windowTrades.Count,
                            WinRate = winRate,
                            ProfitLossRatio = profitLossRatio,
                            NetProfit = stats.NetProfit,
                            ProfitFactor = stats.ProfitFactor,
                            MaxDrawdown = CalculateMaxDrawdown(windowTrades)
                        });
                    }
                    catch (Exception ex)
                    {
                        // 记录但不中断分析过程
                        Console.WriteLine($"滑动窗口分析警告: {windowStart:yyyy-MM-dd} - {ex.Message}");
                    }
                }
            }

            return results;
        }

        /// <summary>
        /// 计算最大回撤
        /// </summary>
        /// <param name="trades">交易数据</param>
        /// <returns>最大回撤</returns>
        private double CalculateMaxDrawdown(List<TradeModel> trades)
        {
            if (trades.Count == 0) return 0;

            double runningBalance = 0;
            double peak = 0;
            double maxDrawdown = 0;

            foreach (var trade in trades.OrderBy(t => t.Time))
            {
                double netProfit = trade.Profit + trade.Swap + trade.Commission + trade.Fee;
                runningBalance += netProfit;
                
                if (runningBalance > peak)
                    peak = runningBalance;
                
                double drawdown = peak - runningBalance;
                if (drawdown > maxDrawdown)
                    maxDrawdown = drawdown;
            }

            return maxDrawdown;
        }

        #endregion

        #region Kelly公式参数提供方法

        /// <summary>
        /// 为Kelly公式提供最新的胜率和盈亏比参数
        /// </summary>
        /// <param name="trades">历史交易数据</param>
        /// <param name="windowDays">分析窗口天数</param>
        /// <returns>Kelly公式参数</returns>
        public KellyParameters GetKellyParameters(IEnumerable<TradeModel> trades, 
            int windowDays = DEFAULT_WINDOW_DAYS)
        {
            if (trades == null)
                throw new ArgumentNullException(nameof(trades));

            // 清洗数据
            var cleanedTrades = CleanHistoricalData(trades);
            
            // 应用时间窗口
            var windowTrades = FilterByTimeWindow(cleanedTrades, windowDays);
            var tradeList = windowTrades.ToList();

            if (tradeList.Count < MIN_SAMPLE_SIZE)
                throw new ArgumentException($"时间窗口内交易数据不足最小样本要求{MIN_SAMPLE_SIZE}");

            // 计算核心参数
            double winRate = CalculateWinRate(tradeList);
            double profitLossRatio = CalculateProfitLossRatio(tradeList);
            var stats = CalculateDetailedProfitLossStats(tradeList);

            // 计算置信区间（基于正态分布近似）
            double winRateStdError = Math.Sqrt(winRate * (1 - winRate) / tradeList.Count);
            double winRateConfidenceInterval = 1.96 * winRateStdError; // 95%置信区间

            return new KellyParameters
            {
                WinRate = winRate,
                OddsRatio = profitLossRatio,
                SampleSize = tradeList.Count,
                WinRateConfidenceInterval = winRateConfidenceInterval,
                TotalProfitableTrades = stats.ProfitableTrades,
                TotalLosingTrades = stats.LosingTrades,
                NetProfit = stats.NetProfit,
                ProfitFactor = stats.ProfitFactor,
                AnalysisStartDate = tradeList.Min(t => t.Time),
                AnalysisEndDate = tradeList.Max(t => t.Time),
                DataQualityScore = CalculateDataQualityScore(tradeList, cleanedTrades.Count())
            };
        }

        /// <summary>
        /// 计算数据质量评分
        /// </summary>
        /// <param name="finalTrades">最终使用的交易数据</param>
        /// <param name="initialTradeCount">初始交易数据量</param>
        /// <returns>数据质量评分 (0-1)</returns>
        private double CalculateDataQualityScore(List<TradeModel> finalTrades, int initialTradeCount)
        {
            if (initialTradeCount == 0) return 0;

            // 数据保留率
            double retentionRate = (double)finalTrades.Count / initialTradeCount;
            
            // 样本充足性
            double sampleAdequacy = Math.Min(1.0, finalTrades.Count / (double)MIN_SAMPLE_SIZE);
            
            // 时间分布均匀性（简化评估）
            double timeSpanScore = 1.0;
            if (finalTrades.Count > 1)
            {
                var timeSpan = finalTrades.Max(t => t.Time) - finalTrades.Min(t => t.Time);
                timeSpanScore = Math.Min(1.0, timeSpan.TotalDays / 30); // 30天为满分
            }

            // 综合评分
            return (retentionRate * 0.4 + sampleAdequacy * 0.4 + timeSpanScore * 0.2);
        }

        #endregion
    }

    #region 数据结构定义

    /// <summary>
    /// 盈亏统计详细信息
    /// </summary>
    public class ProfitLossStatistics
    {
        public int TotalTrades { get; set; }
        public int ProfitableTrades { get; set; }
        public int LosingTrades { get; set; }
        public int BreakEvenTrades { get; set; }
        
        public double AverageProfit { get; set; }
        public double AverageLoss { get; set; }
        
        public double MaxProfit { get; set; }
        public double MaxLoss { get; set; }
        
        public double TotalProfit { get; set; }
        public double TotalLoss { get; set; }
        
        public double NetProfit { get; set; }
        public double ProfitFactor { get; set; }
    }

    /// <summary>
    /// 滑动窗口分析结果
    /// </summary>
    public class SlidingWindowResult
    {
        public DateTime WindowStart { get; set; }
        public DateTime WindowEnd { get; set; }
        public int TradeCount { get; set; }
        public double WinRate { get; set; }
        public double ProfitLossRatio { get; set; }
        public double NetProfit { get; set; }
        public double ProfitFactor { get; set; }
        public double MaxDrawdown { get; set; }
    }

    /// <summary>
    /// Kelly公式参数
    /// </summary>
    public class KellyParameters
    {
        public double WinRate { get; set; }
        public double OddsRatio { get; set; }
        public int SampleSize { get; set; }
        public double WinRateConfidenceInterval { get; set; }
        public int TotalProfitableTrades { get; set; }
        public int TotalLosingTrades { get; set; }
        public double NetProfit { get; set; }
        public double ProfitFactor { get; set; }
        public DateTime AnalysisStartDate { get; set; }
        public DateTime AnalysisEndDate { get; set; }
        public double DataQualityScore { get; set; }
    }

    #endregion
}
